--- title: "Creando tablas en {gt} con Spotify API & Bad Bunny" output: html_document authors: ["kari", "andrea"] date: 2022-11-23 type: post draft: true tags: ["{gt}", "Tablas", "Spotify"] summary: 'En este tutorial vas a aprender cómo obtener información desde Spotify y plasmarla en una tabla usando el paquete {gt} 🐰🎵' ---

Introducción

Utilización del paquete {gt} 📦 para visualizar datos obtenidos mediante la API de Spotify🎶.

Este post hace referencia al taller del 2022-11-23:

🔸 Repositorio 🔸 Grabación 🔸

1. Librerías

Se definen los paquetes 📦 a utilizar:

library(tidyverse) # Manipulación de datos
library(gt)        # Tablas gt: grammar of tables
library(gtExtras)  # Extras de tablas gt
library(spotifyr)  # Trabajar con la API de spotify
library(rjson)     # Lectura de archivo json
library(ggrepel)   # Para textos en ggplot

⚠️ De no haber podido obtener las credenciales para consultar a la API de Spotify, pasar al punto 3, en donde se leen los datos de un dataframe almacenado en el repositorio. ⚠️

2. API Spotify - Credenciales

Se cuenta con un archivo (credentials..json), que contiene las siguientes variables obtenidas de la página de desarrolladores de Spotify:

El formato del archivo credentials.json está incluido en el .gitignore del repositorio. Esto permite mantener las credenciales anónimas (sin publicar en repositorios), dado que son personales y no deben compartirse. A continuación se muestra el formato de este archivo:

{
  "SPOTIFY_CLIENT_ID" : "SPOTIFY_CLIENT_ID",
  "SPOTIFY_CLIENT_SECRET" : "SPOTIFY_CLIENT_SECRET"
}

Se realiza la lectura de las credenciales mediante la función fromJSON(). Luego, se setean como variables de entorno:

credentials <- fromJSON(file = "credentials.json")

Sys.setenv(SPOTIFY_CLIENT_ID = credentials$SPOTIFY_CLIENT_ID)
Sys.setenv(SPOTIFY_CLIENT_SECRET = credentials$SPOTIFY_CLIENT_SECRET)

Se utiliza la función get_spotify_access_token() del paquete {spotifyr} 📦para autenticarse a la API:

access_token <- get_spotify_access_token()

Para verificar que la autenticación haya sido exitosa, se realiza una consulta de los artistas más escuchados:

tabla_user <- get_my_top_artists_or_tracks(
                type = 'artists',
                time_range = 'short_term', # 'medium_term', 'long_term'
                limit = 5
              ) %>%
  select(name, genres) %>%
  rowwise() %>%
  mutate(genres = paste(genres, collapse = ', ')) %>%
  ungroup %>%
  gt()

Caso: análisis de un artista particular

Se define el artista a utilizar:

ARTISTA = 'bad bunny'

La función get_artist_audio_features() del paquete {spotifyr} 📦 permite generar una búsqueda de variables vinculadas al audio de cada track.

vars_audio = c('danceability',
               'energy',
               'loudness',
               'acousticness',
               'instrumentalness')
tracks_features <- get_artist_audio_features(artist = ARTISTA) %>%
  
  select(
    artist_id,
    artist_name,
    album_id,
    album_name,
    album_release_date,
    album_images,
    track_name,
    duration_ms,
    
    # Variables vinculadas al audio:
    all_of(vars_audio)
  ) %>%
  
  arrange(desc(album_release_date))

3. The grammar of tables (gt)

De no haber podido autenticarte con la API de Spotify, dejamos un archivo .rds con los datos necesarios para seguir con el resto del taller:

tracks_features <- read_rds('https://github.com/karbartolome/gt-spotify/blob/main/02_caso_spotify/tracks_features.rds?raw=true')

Se cuenta con un df de variables vinculadas a cada canción de cada álbum del artista seleccionado:

tracks_features %>%
  glimpse()
## Rows: 92
## Columns: 13
## $ artist_id          <chr> "4q3ewBCX7sLwd24euuV69X", "4q3ewBCX7sLwd24euuV69X",…
## $ artist_name        <chr> "Bad Bunny", "Bad Bunny", "Bad Bunny", "Bad Bunny",…
## $ album_id           <chr> "3RQQmkQEvNCY4prGKE6oc5", "3RQQmkQEvNCY4prGKE6oc5",…
## $ album_name         <chr> "Un Verano Sin Ti", "Un Verano Sin Ti", "Un Verano …
## $ album_release_date <chr> "2022-05-06", "2022-05-06", "2022-05-06", "2022-05-…
## $ album_images       <list> [<data.frame[3 x 3]>], [<data.frame[3 x 3]>], [<da…
## $ track_name         <chr> "Moscow Mule", "Después de la Playa", "Me Porto Bon…
## $ duration_ms        <int> 245939, 230400, 178567, 243716, 176936, 230704, 237…
## $ danceability       <dbl> 0.804, 0.564, 0.911, 0.650, 0.787, 0.872, 0.795, 0.…
## $ energy             <dbl> 0.674, 0.903, 0.712, 0.715, 0.546, 0.588, 0.684, 0.…
## $ loudness           <dbl> -5.453, -3.221, -5.105, -5.198, -7.094, -6.276, -3.…
## $ acousticness       <dbl> 0.2940, 0.3560, 0.0901, 0.0993, 0.3050, 0.2750, 0.0…
## $ instrumentalness   <dbl> 0.00000118, 0.00000000, 0.00002680, 0.00029100, 0.0…

3.1 Intro a gt

tabla <- tracks_features %>%
  head() %>%
  select(artist_name, album_name, track_name, all_of(vars_audio)) %>%
  
  # En este paso se transforma el df en un objeto gt:
  gt()
tabla
artist_name album_name track_name danceability energy loudness acousticness instrumentalness
Bad Bunny Un Verano Sin Ti Moscow Mule 0.804 0.674 -5.453 0.2940 0.00000118
Bad Bunny Un Verano Sin Ti Después de la Playa 0.564 0.903 -3.221 0.3560 0.00000000
Bad Bunny Un Verano Sin Ti Me Porto Bonito 0.911 0.712 -5.105 0.0901 0.00002680
Bad Bunny Un Verano Sin Ti Tití Me Preguntó 0.650 0.715 -5.198 0.0993 0.00029100
Bad Bunny Un Verano Sin Ti Un Ratito 0.787 0.546 -7.094 0.3050 0.00004550
Bad Bunny Un Verano Sin Ti Yo No Soy Celoso 0.872 0.588 -6.276 0.2750 0.00000000

Notar que las variables numéricas aparecen con muchos decimales. Una forma sencilla de arreglar esto en {gt} es la siguiente:

tabla %>%
  fmt_number(columns = where(is.numeric), decimals = 2)

3.2 Tabla a nivel álbumes

Se agregan los datos a nivel de cada álbum. Notar que, en el caso de las variables de audio, se decidió convertirlas en una lista (N observaciones numéricas que representan cada una de las canciones incluidas en el álbum). En los próximos pasos se explicará el por qué de la decisión:

df_albums <- tracks_features %>%
  
  group_by(album_images,
           artist_name,
           album_id,
           album_name,
           album_release_date) %>%
  
  summarise(# Duración del álbum: suma de duración de cada canción
    duration_mins = sum(duration_ms / (1000 * 60)),
    
    # Lista de cada variable de audio
    across(all_of(vars_audio), ~ list(.x)),) %>%
  
  ungroup()
tabla_albums <- df_albums %>%
  select(-album_images) %>%
  gt() 
tabla_albums
artist_name album_id album_name album_release_date duration_mins danceability energy loudness acousticness instrumentalness
Bad Bunny 3RQQmkQEvNCY4prGKE6oc5 Un Verano Sin Ti 2022-05-06 81.85270 0.804, 0.564, 0.911, 0.650, 0.787, 0.872, 0.795, 0.876, 0.663, 0.801, 0.829, 0.861, 0.810, 0.647, 0.817, 0.629, 0.777, 0.839, 0.797, 0.714, 0.503, 0.849, 0.608 0.674, 0.903, 0.712, 0.715, 0.546, 0.588, 0.684, 0.498, 0.786, 0.475, 0.799, 0.645, 0.789, 0.686, 0.670, 0.698, 0.595, 0.690, 0.616, 0.645, 0.496, 0.584, 0.626 -5.453, -3.221, -5.105, -5.198, -7.094, -6.276, -3.971, -7.511, -3.510, -8.797, -5.389, -6.960, -5.080, -5.745, -6.534, -4.485, -6.790, -4.464, -10.229, -5.493, -9.123, -8.195, -4.773 0.2940, 0.3560, 0.0901, 0.0993, 0.3050, 0.2750, 0.0225, 0.0706, 0.2290, 0.1410, 0.0151, 0.4230, 0.1910, 0.0800, 0.1230, 0.0509, 0.5900, 0.2070, 0.7620, 0.2290, 0.6930, 0.0929, 0.5970 0.00000118, 0.00000000, 0.00002680, 0.00029100, 0.00004550, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00001730, 0.00054300, 0.00059200, 0.00001080, 0.00000134, 0.00000192, 0.00166000, 0.00139000, 0.00000833, 0.00029300, 0.00083000, 0.00000000, 0.00000618, 0.00000216
Bad Bunny 2d9BCZeAAhiZWPpbX9aPCW EL ÚLTIMO TOUR DEL MUNDO 2020-11-27 47.33332 0.716, 0.811, 0.860, 0.762, 0.856, 0.763, 0.688, 0.584, 0.883, 0.643, 0.731, 0.430, 0.612, 0.636, 0.693, 0.764 0.522, 0.637, 0.725, 0.861, 0.618, 0.597, 0.881, 0.411, 0.600, 0.727, 0.573, 0.412, 0.561, 0.656, 0.549, 0.523 -6.834, -4.835, -6.700, -4.075, -4.892, -5.054, -4.889, -7.147, -7.188, -5.506, -10.059, -5.372, -6.054, -5.542, -6.098, -7.371 0.1660, 0.2340, 0.0464, 0.1390, 0.0303, 0.1580, 0.0947, 0.8690, 0.1140, 0.0439, 0.4010, 0.2000, 0.0601, 0.4510, 0.5050, 0.7770 0.00006500, 0.00057200, 0.00009100, 0.00000108, 0.00000000, 0.00001830, 0.00000000, 0.00004470, 0.00000794, 0.00000000, 0.00005220, 0.00000000, 0.00000000, 0.00000797, 0.00004730, 0.00000000
Bad Bunny 4gvQO5mEuhbMSrLIuwXkmz LAS QUE NO IBAN A SALIR 2020-05-10 30.31767 0.869, 0.865, 0.712, 0.750, 0.772, 0.590, 0.828, 0.813, 0.856, 0.779 0.727, 0.500, 0.599, 0.741, 0.830, 0.729, 0.581, 0.616, 0.656, 0.764 -7.397, -10.805, -6.342, -4.430, -5.019, -4.950, -9.909, -8.867, -6.483, -8.217 0.0589, 0.0122, 0.4120, 0.4470, 0.0160, 0.3210, 0.0378, 0.0926, 0.1650, 0.2210 0.0099100, 0.0000773, 0.0000215, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0006690
Bad Bunny 5lJqux7orBlA1QzyiBGti1 YHLQMDLG 2020-02-29 65.96717 0.900, 0.685, 0.882, 0.744, 0.860, 0.737, 0.710, 0.762, 0.802, 0.886, 0.850, 0.860, 0.550, 0.607, 0.761, 0.810, 0.867, 0.820, 0.683, 0.631 0.603, 0.848, 0.612, 0.868, 0.758, 0.766, 0.810, 0.801, 0.787, 0.672, 0.536, 0.791, 0.598, 0.829, 0.732, 0.854, 0.686, 0.635, 0.677, 0.665 -5.313, -4.561, -6.103, -4.524, -5.160, -4.242, -5.507, -3.661, -6.181, -4.394, -5.161, -4.784, -5.402, -4.074, -5.469, -3.999, -5.342, -5.749, -4.496, -7.589 0.4020, 0.0861, 0.0475, 0.0326, 0.0210, 0.3100, 0.1170, 0.2000, 0.0832, 0.0363, 0.2670, 0.1690, 0.1320, 0.0103, 0.0920, 0.0732, 0.3110, 0.4090, 0.0869, 0.6020 0.00000481, 0.00000714, 0.00000507, 0.00000000, 0.00006540, 0.00000663, 0.00013200, 0.00007040, 0.00000000, 0.00000878, 0.00003890, 0.00010600, 0.00000258, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000170
Bad Bunny 6ylFfzx32ICw4L1A7YWNLN OASIS 2019-06-28 31.10840 0.450, 0.785, 0.845, 0.639, 0.754, 0.627, 0.740, 0.825 0.842, 0.792, 0.813, 0.791, 0.646, 0.675, 0.698, 0.807 -3.602, -4.695, -2.979, -4.443, -5.795, -7.125, -3.838, -5.753 0.0171, 0.0104, 0.1110, 0.0275, 0.1520, 0.1250, 0.2150, 0.0732 0.00000000, 0.00000000, 0.00241000, 0.00000000, 0.00001850, 0.00000000, 0.00000000, 0.00000479
Bad Bunny 7CjJb2mikwAWA1V6kewFBF X 100PRE 2018-12-23 53.94298 0.826, 0.850, 0.727, 0.651, 0.855, 0.514, 0.785, 0.672, 0.743, 0.787, 0.655, 0.767, 0.600, 0.759, 0.816 0.449, 0.500, 0.580, 0.575, 0.512, 0.646, 0.626, 0.594, 0.730, 0.705, 0.725, 0.379, 0.528, 0.536, 0.538 -8.330, -6.708, -6.804, -7.739, -8.165, -8.833, -8.030, -6.813, -4.314, -7.582, -5.497, -10.348, -6.554, -6.663, -6.354 0.5560, 0.0139, 0.1850, 0.6270, 0.1460, 0.1970, 0.1580, 0.0626, 0.2870, 0.1370, 0.0327, 0.6680, 0.2630, 0.8210, 0.0142 0.00001850, 0.00038400, 0.00000358, 0.00020200, 0.00085400, 0.00002300, 0.00000460, 0.00005140, 0.00008100, 0.00000106, 0.00264000, 0.00014500, 0.00000000, 0.00000457, 0.00049600

3.3 Imágenes en tablas gt

Se quiere añadir la imagen del álbum. Para ello, se construye la siguiente función. Para cada álbum se cuenta con distintos tamaños de imágenes, donde cada imagen se encuentra representada por un url. Se decide seleccionar la imagen de tamaño=64, extrayendo el url:

get_imagen = function(album_images) {
  album_images %>%
    data.frame() %>%
    filter(height == 64) %>%
    pull(url) %>%
    as.character()
}

Se aplica la función al dataframe. Para ello, se utiliza la función map() del paquete {purrr} 📦:

df_albums <- df_albums %>%
  mutate(album_images = map(album_images, ~ get_imagen(album_images = .x))) %>%
  distinct()

Para visualizar los urls como imagenes, se utiliza la función text_transform() de {gt}. Además, la función tab_header() permite añadir título y subtítulo.

tabla_albums <- df_albums %>%  select(-album_id) %>%
  
  gt() %>%
  
  tab_header(title = md(glue::glue('**{str_to_title(ARTISTA)}** en Spotify')),
             subtitle = 'Álbumes más recientes') %>%
  
  text_transform(
    locations = cells_body(columns = c(album_images)),
    fn = function(album_images) {
      lapply(album_images, web_image, height = 50)
    }
  ) 

De momento, se ocultan las variables de audio para visualizar la tabla:

tabla_albums %>%
  cols_hide(all_of(vars_audio))
Bad Bunny en Spotify
Álbumes más recientes
album_images artist_name album_name album_release_date duration_mins
Bad Bunny Un Verano Sin Ti 2022-05-06 81.85270
Bad Bunny EL ÚLTIMO TOUR DEL MUNDO 2020-11-27 47.33332
Bad Bunny LAS QUE NO IBAN A SALIR 2020-05-10 30.31767
Bad Bunny YHLQMDLG 2020-02-29 65.96717
Bad Bunny OASIS 2019-06-28 31.10840
Bad Bunny X 100PRE 2018-12-23 53.94298

Otra función útil para visualizar este tipo de información es gt_merge_stack() que permite concatenar dos variables en una única:

tabla_albums <- tabla_albums %>%
  
  gt_merge_stack(col1 = album_name,
                 col2 = artist_name) 
tabla_albums %>% 
  cols_hide(all_of(vars_audio)) 
Bad Bunny en Spotify
Álbumes más recientes
album_images album_name album_release_date duration_mins
Un Verano Sin Ti
Bad Bunny
2022-05-06 81.85270
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
2020-11-27 47.33332
LAS QUE NO IBAN A SALIR
Bad Bunny
2020-05-10 30.31767
YHLQMDLG
Bad Bunny
2020-02-29 65.96717
OASIS
Bad Bunny
2019-06-28 31.10840
X 100PRE
Bad Bunny
2018-12-23 53.94298

Para visualizar las variables de audio, se utilizarán algunas funcionalidades del paquete {gtExtras} 📦:

tabla_albums <- tabla_albums %>%
  
  gt_color_box(
    columns = duration_mins,
    palette = c('white', color_spotify),
    domain = c(0, round(max(df_albums$duration_mins)) + 1)
  ) %>%
  
  gt_plt_dist(
    column = danceability,
    type = "density",
    line_color = "black",
    fill_color = color_spotify
  ) %>%
  
  gt_plt_dist(
    column = energy,
    type = "density",
    line_color = "black",
    fill_color = color_spotify
  ) %>%
  
  gt_plt_dist(
    column = loudness,
    type = "density",
    line_color = "black",
    fill_color = color_spotify
  ) %>%
  
  gt_plt_dist(
    column = acousticness,
    type = "boxplot",
    line_color = "black",
    fill_color = color_spotify
  )
tabla_albums %>%
  cols_hide('instrumentalness')
Bad Bunny en Spotify
Álbumes más recientes
album_images album_name album_release_date duration_mins danceability energy loudness acousticness
Un Verano Sin Ti
Bad Bunny
2022-05-06
82
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
2020-11-27
47
LAS QUE NO IBAN A SALIR
Bad Bunny
2020-05-10
30
YHLQMDLG
Bad Bunny
2020-02-29
66
OASIS
Bad Bunny
2019-06-28
31
X 100PRE
Bad Bunny
2018-12-23
54

3.4 Ggplot en gt

Notar que si se agregara el boxplot para el caso de la variable instrumentalness, se observa, para todos los álbumes, distribuciones con valores atípicos:

tabla_albums %>%
  gt_plt_dist(
    column = instrumentalness,
    type = "boxplot",
    line_color = "black",
    fill_color = color_spotify
  ) 

Por esta razón, se utilizará el caso de esta variable para explicar cómo se podría identificar a estos valores atípicos mediante el uso de {ggplot2} 📦

gen_outliers_plots <- function(.df,
                               .variable,
                               .font_size = 22,
                               .lwd = 3) {
  temp <- .df %>%
    select(all_of(c('track_name', .variable))) %>%
    pivot_longer(cols = -track_name)
  
  track <- temp %>%
    slice(which.max(value)) %>%
    pull(track_name)
  
  temp %>%
    mutate(is_outlier = ifelse(track_name == track, track_name, NA)) %>%
    
    ggplot(aes(y = value, x = name)) +
    geom_boxplot(
      fill = color_spotify,
      width = 0.2,
      lwd = .lwd,
      outlier.size = 5
    ) +
    geom_text_repel(
      aes(label = is_outlier),
      na.rm = TRUE,
      nudge_x = 0.4,
      size = .font_size
    ) +
    coord_flip() +
    theme_void()
}

Realizando la prueba de la función, se observa el gráfico que genera:

gen_outliers_plots(
  .df = tracks_features %>% filter(album_name == 'X 100PRE'),
  .variable = 'instrumentalness',
  .font_size = 10,
  .lwd = 0.5
)

Ahora se incluyen estos plots en la tabla. Primero, se mappea la función al df:

df_albums <- df_albums %>%
  mutate(instrumentalness = map(
    album_id,
    ~ gen_outliers_plots(.df = tracks_features %>% filter(album_id == .x),
                         .variable = 'instrumentalness')
  ))

Se visualiza la tabla completa:

tabla_albums <- tabla_albums %>%
  
  text_transform(
    locations = cells_body(columns = instrumentalness),
    fn = function(x) {
      map(
        df_albums$instrumentalness,
        gt::ggplot_image,
        height = px(60),
        aspect_ratio = 2
      )
    }
  ) 
tabla_albums
Bad Bunny en Spotify
Álbumes más recientes
album_images album_name album_release_date duration_mins danceability energy loudness acousticness instrumentalness
Un Verano Sin Ti
Bad Bunny
2022-05-06
82
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
2020-11-27
47
LAS QUE NO IBAN A SALIR
Bad Bunny
2020-05-10
30
YHLQMDLG
Bad Bunny
2020-02-29
66
OASIS
Bad Bunny
2019-06-28
31
X 100PRE
Bad Bunny
2018-12-23
54

Formato

tabla_albums <- tabla_albums %>%
  
  gt::tab_spanner(label = 'Un tipo de variables',
                  columns = danceability:loudness) %>%
  gt::tab_spanner(label = 'Otro tipo de variables',
                  columns = c('acousticness', 'instrumentalness')) %>%
  
  gt::tab_footnote(locations = cells_column_labels('duration_mins'),
                   footnote = 'Duración en minutos = suma de la duración de 
                               cada una de las canciones que componen el álbum.'
                   ) %>%
  
  gt::tab_footnote(locations = cells_column_labels('instrumentalness'),
                   footnote = 'En el caso de instrumentalness, al existir 
                               valores muy atípicos se muestra la canción a la 
                               que corresponde el máximo valor en cada álbum.'
                   ) %>%
  
  gt::tab_source_note(source_note = 'Fuente: API de Spotify') %>%
  
  cols_label(
    album_images = '',
    album_name = '',
    album_release_date = 'Lanzamiento',
    duration_mins = 'Duración',
    danceability = 'Danceability 🕺',
    energy = 'Energy ✨',
    loudness = 'Loudness 🔊',
    acousticness = 'Acousticness 🎹',
    instrumentalness = 'Instrumentalness 🎼'
  )  
tabla_albums
Bad Bunny en Spotify
Álbumes más recientes
Lanzamiento Duración1 Un tipo de variables Otro tipo de variables
Danceability 🕺 Energy ✨ Loudness 🔊 Acousticness 🎹 Instrumentalness 🎼2
Un Verano Sin Ti
Bad Bunny
2022-05-06
82
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
2020-11-27
47
LAS QUE NO IBAN A SALIR
Bad Bunny
2020-05-10
30
YHLQMDLG
Bad Bunny
2020-02-29
66
OASIS
Bad Bunny
2019-06-28
31
X 100PRE
Bad Bunny
2018-12-23
54
Fuente: API de Spotify
1 Duración en minutos = suma de la duración de cada una de las canciones que componen el álbum.
2 En el caso de instrumentalness, al existir valores muy atípicos se muestra la canción a la que corresponde el máximo valor en cada álbum.

Además, se añade un theme específico para asignarle colores y formatos adicionales:

my_theme <- function(gt_object, ...) {
  gt_object %>%
    tab_options(
      column_labels.background.color = "#39423c",
      footnotes.background.color = "#39423c",
      source_notes.background.color = "#39423c",
      heading.background.color = "#39423c",
      heading.align = "left",
      ...
    ) %>%
    tab_style(style = cell_text(color = color_spotify, size = px(32)),
              locations = cells_title("title"))
}
tabla_albums <- tabla_albums %>%
  my_theme()
tabla_albums
Bad Bunny en Spotify
Álbumes más recientes
Lanzamiento Duración1 Un tipo de variables Otro tipo de variables
Danceability 🕺 Energy ✨ Loudness 🔊 Acousticness 🎹 Instrumentalness 🎼2
Un Verano Sin Ti
Bad Bunny
2022-05-06
82
EL ÚLTIMO TOUR DEL MUNDO
Bad Bunny
2020-11-27
47
LAS QUE NO IBAN A SALIR
Bad Bunny
2020-05-10
30
YHLQMDLG
Bad Bunny
2020-02-29
66
OASIS
Bad Bunny
2019-06-28
31
X 100PRE
Bad Bunny
2018-12-23
54
Fuente: API de Spotify
1 Duración en minutos = suma de la duración de cada una de las canciones que componen el álbum.
2 En el caso de instrumentalness, al existir valores muy atípicos se muestra la canción a la que corresponde el máximo valor en cada álbum.

3.5 Guardar la tabla gt como archivo .png

Para guardar la tabla, se utiliza la función gtsave() de {gt} 📦:

gt::gtsave(tabla_albums,
           'tablas/tabla_final.png',
           vwidth = 2000,
           vheight = 3000)